/*
 * Copyright (C) 2007 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.eclipse.che.ide.util;

import java.io.Serializable;
import java.util.Comparator;

/**
 * An immutable, semantic-free ordered pair of nullable values. These can be accessed using the
 * {@link #getFirst} and {@link #getSecond} methods. Equality and hashing are defined in the natural
 * way.
 *
 * <p>
 *
 * <p>This type is devoid of semantics, best used for simple mechanical aggregations of unrelated
 * values in implementation code. Avoid using it in your APIs, preferring an explicit type that
 * conveys the exact semantics of the data. For example, instead of:
 *
 * <pre>{@code
 * <p/>
 *   Pair<T, T> findMinAndMax(List<T> list) {...}
 * }</pre>
 *
 * <p>... use:
 *
 * <pre>{@code
 * <p/>
 *   Range<T> findRange(List<T> list) {...}
 * }</pre>
 *
 * <p>This usually involves creating a new custom value-object type. This is difficult to do "by
 * hand" in Java, but avoid the temptation to extend {@code Pair} to accomplish this; consider using
 * the utilities {@link com.google.common.labs.misc.ComparisonKeys} or {@link
 * com.google.common.labs.misc.ValueType} to help you with this instead.
 */
public class Pair<A, B> implements Serializable {

  /** Creates a new pair containing the given elements in order. */
  public static <A, B> Pair<A, B> of(A first, B second) {
    return new Pair<A, B>(first, second);
  }

  /** The first element of the pair; see also {@link #getFirst}. */
  public final A first;

  /** The second element of the pair; see also {@link #getSecond}. */
  public final B second;

  /** Constructor. It is usually easier to call {@link #of}. */
  public Pair(A first, B second) {
    this.first = first;
    this.second = second;
  }

  /** Returns the first element of this pair; see also {@link #first}. */
  public A getFirst() {
    return first;
  }

  /** Returns the second element of this pair; see also {@link #second}. */
  public B getSecond() {
    return second;
  }

  /** Returns a function that yields {@link #first}. */
  @SuppressWarnings("unchecked")
  public static <A, B> Function<Pair<A, B>, A> firstFunction() {
    // The safety of the unchecked conversion here is implied by the
    // implementation of the PairFirstFunction which always returns the first
    // element from a pair (which for Pair<A, B> is of type A).
    return (Function) PairFirstFunction.INSTANCE;
  }

  /** Returns a function that yields {@link #second}. */
  @SuppressWarnings("unchecked")
  public static <A, B> Function<Pair<A, B>, B> secondFunction() {
    // The safety of the unchecked conversion here is implied by the
    // implementation of the PairSecondFunction which always returns the second
    // element from a pair (which for Pair<A, B> is of type B).
    return (Function) PairSecondFunction.INSTANCE;
  }

  /*
   * If we use the enum singleton pattern for these functions, Flume's type
   * inference chokes: http://b/4863010
   */

  private static final class PairFirstFunction<A, B>
      implements Function<Pair<A, B>, A>, Serializable {
    static final PairFirstFunction<Object, Object> INSTANCE =
        new PairFirstFunction<Object, Object>();

    @Override
    public A apply(Pair<A, B> from) {
      return from.getFirst();
    }

    private Object readResolve() {
      return INSTANCE;
    }
  }

  private static final class PairSecondFunction<A, B>
      implements Function<Pair<A, B>, B>, Serializable {
    static final PairSecondFunction<Object, Object> INSTANCE =
        new PairSecondFunction<Object, Object>();

    @Override
    public B apply(Pair<A, B> from) {
      return from.getSecond();
    }

    private Object readResolve() {
      return INSTANCE;
    }
  }

  /**
   * Returns a comparator that compares two Pair objects by comparing the result of {@link
   * #getFirst()} for each.
   */
  @SuppressWarnings("unchecked")
  public static <A extends Comparable, B> Comparator<Pair<A, B>> compareByFirst() {
    return (Comparator) FirstComparator.FIRST_COMPARATOR;
  }

  /**
   * Returns a comparator that compares two Pair objects by comparing the result of {@link
   * #getSecond()} for each.
   */
  @SuppressWarnings("unchecked")
  public static <A, B extends Comparable> Comparator<Pair<A, B>> compareBySecond() {
    return (Comparator) SecondComparator.SECOND_COMPARATOR;
  }

  // uses raw Comparable to support classes defined without generics
  @SuppressWarnings("unchecked")
  private enum FirstComparator implements Comparator<Pair<Comparable, Object>> {
    FIRST_COMPARATOR;

    @Override
    public int compare(Pair<Comparable, Object> pair1, Pair<Comparable, Object> pair2) {
      return pair1.getFirst().compareTo(pair2.getFirst());
    }
  }

  // uses raw Comparable to support classes defined without generics
  @SuppressWarnings("unchecked")
  private enum SecondComparator implements Comparator<Pair<Object, Comparable>> {
    SECOND_COMPARATOR;

    @Override
    public int compare(Pair<Object, Comparable> pair1, Pair<Object, Comparable> pair2) {
      return pair1.getSecond().compareTo(pair2.getSecond());
    }
  }

  // TODO: decide what level of commitment to make to this impl
  @Override
  public boolean equals(Object object) {
    // TODO: it is possible we want to change this to
    // if (object != null && object.getClass() == getClass()) {
    if (object instanceof Pair) {
      Pair<?, ?> that = (Pair<?, ?>) object;
      return equal(this.first, that.first) && equal(this.second, that.second);
    }
    return false;
  }

  @Override
  public int hashCode() {
    int hash1 = first == null ? 0 : first.hashCode();
    int hash2 = second == null ? 0 : second.hashCode();
    return 31 * hash1 + hash2;
  }

  /**
   * {@inheritDoc}
   *
   * <p>
   *
   * <p>This implementation returns a string in the form {@code (first, second)}, where {@code
   * first} and {@code second} are the String representations of the first and second elements of
   * this pair, as given by {@link String#valueOf(Object)}. Subclasses are free to override this
   * behavior.
   */
  @Override
  public String toString() {
    // GWT doesn't support String.format().
    return "(" + first + ", " + second + ")";
  }

  /**
   * Determines whether two possibly-null objects are equal. Returns:
   *
   * <p>
   *
   * <ul>
   *   <li>{@code true} if {@code a} and {@code b} are both null.
   *   <li>{@code true} if {@code a} and {@code b} are both non-null and they are equal according to
   *       {@link Object#equals(Object)}.
   *   <li>{@code false} in all other situations.
   * </ul>
   *
   * <p>
   *
   * <p>This assumes that any non-null objects passed to this function conform to the {@code
   * equals()} contract.
   */
  private boolean equal(Object a, Object b) {
    return a == b || (a != null && a.equals(b));
  }

  private static final long serialVersionUID = 747826592375603043L;
}
